﻿#region License
/*

	Copyright (C) 2008 Oliver Charles, Daniel Hollander

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
	
*/
#endregion

using Moebius.Controllers;
using Moebius.Input;
using Moebius.Rendering;
using System.Collections.Generic;
using System.Drawing;
using System.Resources;
using System.Security.Permissions;
using System;
using Tao.Sdl;

[assembly: CLSCompliant(true)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, Execution = true)]
[assembly: PermissionSet(SecurityAction.RequestMinimum, Name = "Nothing")]
[assembly: NeutralResourcesLanguage("en-GB")]
namespace Moebius.Core
{
	/// <summary>
	/// The Engine is set of tasks that executed every frame; such as rendering, input and sound. The
	/// Engine also handles the main loop of the game, delegating the process of execution to each
	/// task added to it.
	/// </summary>
	public class Engine : IDisposable
	{
		#region Private Variables

		private IntPtr windowHandle;
		private Stopwatch stopwatch;
		private bool running;
		private InputHandler inputHandler;
        private readonly Dictionary<Type, Task> tasks = new Dictionary<Type, Task>();
        private bool disposed;

		#endregion
		#region Properties

		private ResourceFactory mResourceFactory;
        private readonly ControllerCollection mControllerCollection = new ControllerCollection();
	    private ILogger mLogger;

	    /// <summary>
		/// Gets the main <see cref="ResourceFactory"/> for the Engine.
		/// </summary>
		public ResourceFactory ResourceFactory
		{
			get { return mResourceFactory; }
		}

		/// <summary>
		/// Gets the <see cref="ControllerCollection"/> for this Engine.
		/// </summary>
		public ControllerCollection ControllerCollection
		{
			get { return mControllerCollection; }
		}

        /// <summary>
        /// Gets or sets the logger used throughout the engine.
        /// </summary>
	    public ILogger Logger
	    {
	        get { return mLogger; }
	        set { mLogger = value; }
	    }

	    #endregion
		
		#region Initialize

		/// <summary>
		/// Initialize the game Engine ready for execution. This creates an input handler, and
		/// a suitable graphics device, and then attaches these to a Form.
		/// </summary>
		public void Initialize(RenderingDevice renderingDevice, InputHandler input, string applicationTitle)
		{
			if (renderingDevice == null) throw new ArgumentNullException("renderingDevice");
			if (input == null) throw new ArgumentNullException("input");

			if (Logger != null) Logger.LogInfo("Engine now initializing");

			// Initialize SDL
			if(Logger != null) Logger.LogInfo("Initializing SDL");
			Sdl.SDL_Init(Sdl.SDL_INIT_VIDEO);
			Sdl.SDL_SetVideoMode(800, 600, 0, Sdl.SDL_RESIZABLE | renderingDevice.ExtraSdlOptions);
			Sdl.SDL_WM_SetCaption(applicationTitle, null);

			// Get the window handle
			//Sdl.SDL_SysWMinfo_Unix unixWmInfo;
			//if(Sdl.SDL_GetWMInfo(out unixWmInfo) == 1)
			//  {
			//    windowHandle = unixWmInfo.window;
			//    Console.WriteLine("Found unix window");
			//  }
			windowHandle = IntPtr.Zero;
			

			// Initialize Renderer
			if (Logger != null) Logger.LogInfo("Initializing rendering device");
			renderingDevice.Initialize(windowHandle, new Size(800, 600), new GraphicsOptions());
			AddTask(renderingDevice);

			// Setup input
			inputHandler = input;

			// Setup controller manager
			AddTask(ControllerCollection);

			// Setup resource factory
			mResourceFactory = new ResourceFactory();
			mResourceFactory.RegisterResourceManager(new string[] { ".tiles" }, new ImageAtlasResourceManager());

			renderingDevice.RegisterResourceManager(mResourceFactory);

			stopwatch = new Stopwatch();
		}

		#endregion
		#region Run
		
		public void Run()
		{
			running = true;
			Sdl.SDL_Event sdlEvent;
			
			while(running)
			{
				double elapsedTime = stopwatch.Lap();

				foreach(Task task in tasks.Values)
					task.Execute(elapsedTime);

			    while (Sdl.SDL_PollEvent(out sdlEvent) == 1)
			        switch (sdlEvent.type)
			        {
			            case Sdl.SDL_QUIT:
			                running = false;
			                break;

			            default:
			                inputHandler.HandleEvent(sdlEvent);
			                break;
			        }
			}
			
			Sdl.SDL_Quit();
		}
		
		#endregion
		#region Task Management

		/// <summary>
		/// Add a subtask to the <see cref="Engine"/>
		/// </summary>
		/// <param name="task">The task to add to the Engine</param>
		public void AddTask(Task task)
		{
			Type taskType = task.GetType();
			if(!tasks.ContainsKey(taskType))
				tasks.Add(task.GetType(), task);
		}

		/// <summary>
		/// Find a specific task in this <see cref="Engine"/>
		/// </summary>
		/// <typeparam name="TTaskType">The type of the <see cref="Task"/> to find</typeparam>
		/// <returns>A instance of the <see cref="Task"/> of type <typeparamref name="TTaskType"/></returns>
		public TTaskType FindTask<TTaskType>() where TTaskType : Task
		{
			Type taskType = typeof(TTaskType);
            if (tasks.ContainsKey(taskType))
                return (TTaskType)tasks[taskType];
            else
                foreach (Type t in tasks.Keys)
                    if (t.IsSubclassOf(taskType))
                        return (TTaskType) tasks[t];

		    return null;
		}

		#endregion

	    #region IDisposable Members

	    public void Dispose()
	    {
	        Dispose(true);
            GC.SuppressFinalize(this);
	    }

	    private void Dispose(bool disposing)
	    {
	        if(disposing)
	        {
	            if(!disposed)
	            {
	                
	            }

                Sdl.SDL_Quit();

	            disposed = true;
	        }
	    }

        ~Engine()
        {
            Dispose(false);
        }

	    #endregion

	    public void Terminate()
	    {
	        running = false;
	    }
	}
}